package com.hdp.kotlin_samples.middle

/**
 * author:hdp
 * on:2019/6/13:11:03
 * desc:用类委托来快速实现装饰器模式
 *
 * 通过继承的实现容易导致脆弱性，例如如果需要修改其他类的一些行为，
 * 这时候Java中的一种策略是采用装饰器模式：创建一个新类，实现与原始类一样的接口并将原来的类的实例作为一个成员变量。
 * 与原始类拥有相同行为的方法不用修改，只需要直接转发给原始类的实例。
 *
 * 1、通过kotlin的代理，关注需要实现的方法即可，减少大量的模板代码
 *
 */
fun main() {
    val set = mutableSetOf("java", "kotiln", "python")
    println("========生成的set：$set")
    val countingSet2 = CountingSet2(set)
    println("========生成的countingSet$countingSet2")
    countingSet2.add("javaScript")
    countingSet2.addAll(listOf("scale", "c", "c++"))
    println("========添加的元素之后的countingSet:$countingSet2")
    println("========添加的元素个数是：${countingSet2.objectAdded}")
}


/*
* 常见的装饰器模式，为了修改部分的函数，却需要实现所有的接口函数
* */
class CountingSet<T>(val innerSet: MutableCollection<T>) : MutableCollection<T> {

    var objectAdded = 0

    override val size: Int
        get() = innerSet.size

    /*
    * 需要修改的方法
    * */
    override fun add(element: T): Boolean {
        objectAdded++
        return innerSet.add(element)
    }

    /*
    * 需要修改的方法
    * */
    override fun addAll(elements: Collection<T>): Boolean {
        objectAdded += elements.size
        return innerSet.addAll(elements)
    }

    override fun contains(element: T): Boolean {
        return innerSet.contains(element)
    }

    override fun containsAll(elements: Collection<T>): Boolean {
        return innerSet.containsAll(elements)
    }

    override fun isEmpty(): Boolean {
        return innerSet.isEmpty()
    }

    override fun clear() {
        innerSet.clear()
    }

    override fun iterator(): MutableIterator<T> {
        return innerSet.iterator()
    }

    override fun remove(element: T): Boolean {
        return innerSet.remove(element)
    }

    override fun removeAll(elements: Collection<T>): Boolean {
        return innerSet.removeAll(elements)
    }

    override fun retainAll(elements: Collection<T>): Boolean {
        return innerSet.retainAll(elements)
    }
}

/**
 *想要修改HashSet的某些行为函数add和addAll，需要实现MutableCollection接口的所有方法，将这些方法转发给innerSet去具体的实现。虽然只需要修改其中的两个方法，其他代码都是模版代码。
 *只要是重复的模版代码，Kotlin这种全新的语法糖就会想办法将它放在编译阶段再去生成。
 *这时候可以用到类委托by关键字，如下所示：
 */
/*
* 通过by关键字将接口的实现委托给innerSet成员变量，需要修改的函数再去override就可以了
* */
class CountingSet2<T>(val innerSet: MutableCollection<T>) : MutableCollection<T> by innerSet {

    var objectAdded = 0
    override fun add(element: T): Boolean {
        objectAdded++
        return innerSet.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        objectAdded += elements.size
        return innerSet.addAll(elements)
    }

    override fun toString(): String {
        return "$innerSet;addCount:$objectAdded"
    }

    public fun getAddCount(): Int {
        return objectAdded
    }
}
